﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XbdSoft.Client;
using XbdSoft.Contract;
using System.ComponentModel;
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Unity;
using XbdSoft.Domain;
using System.Collections.ObjectModel;
using XbdSoft.WPFVersion.Common;
using System.Windows.Threading;
using System.IO;
using System.Threading;
using System.Windows.Input;
using Microsoft.Practices.Composite.Presentation.Commands;

namespace XbdSoft.WPFVersion.ViewModel
{
    public class DownloadViewModel:INotifyPropertyChanged
    {
        #region Fields 
        protected readonly IEventAggregator EventAggregator = null;
        protected readonly IUnityContainer Container = null;
        #endregion

        #region Ctor

        public DownloadViewModel(IEventAggregator eventAggregator, IUnityContainer container)
        {
            EventAggregator = eventAggregator;
            Container = container;
            Initialize();
            ContinueDownloadCommand = new DelegateCommand<object>(OnContinueDownload, o => true);
            SelectAllResourceCommand = new DelegateCommand<object>(OnSelectAllResource, o => true);
            SelectOthersResourceCommand = new DelegateCommand<object>(OnSelectOthersResource, o => true);

            DownloadSelectedCommand = new DelegateCommand<object>(OnDownloadSelected);
            DeleteSelectedCommand = new DelegateCommand<object>(OnDeleteSelected);
            PauseSelectedCommand = new DelegateCommand<object>(OnPauseSelected);
            OpenDownloadLocationCommand = new DelegateCommand<object>(OnOpenDownloadLocation);
        }

        #endregion
         
        #region Initialize
        protected void Initialize()
        {
            LoadLastDownloadInfo();

            DownloadResources.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(DownloadResources_CollectionChanged);
        }

        protected void DownloadResources_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            OnPropertyChanged("SelectedStatusDownloads");
        }

        #endregion

        #region INotifyPropertyChanged Members

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this,new PropertyChangedEventArgs (propertyName));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Properties
        private ObservableCollection<DownloadDTO> downloadResources = new ObservableCollection<DownloadDTO>();
        public ObservableCollection<DownloadDTO> DownloadResources
        {
            get { return downloadResources; }
            set {
                downloadResources = value;
                OnPropertyChanged("DownloadResources");
            }
        }
        
        public IEnumerable<DownloadDTO> SelectedStatusDownloads
        {
            get { return DownloadResources.Where(d=>(d.Status&SelectedStatus)>0).OrderBy(r=>r.Status); }
        }

        private DownloadStatus selectedStatus = DownloadStatus.All;
        public DownloadStatus SelectedStatus
        {
            get 
            {
                return selectedStatus;
            }
            set
            {
                selectedStatus = value;
                OnPropertyChanged("SelectedStatus");
                OnPropertyChanged("SelectedStatusDownloads");
            }
        }


        public Dictionary<DownloadStatus, string> DownloadStatusValueMapping
        {
            get { return Util.DownloadStatusValueMapping; }
        }


        public DownloadDTO SelectedResource
        {
            get 
            {
                var selectedStatusDownloads =SelectedStatusDownloads;
                if (selectedResourceIndex >= 0 && selectedStatusDownloads.Count() > selectedResourceIndex)
                    return selectedStatusDownloads.ElementAt(selectedResourceIndex);
                return DownloadDTO.Empty;
            }
        }
        private int selectedResourceIndex = -1;
        public int SelectedResourceIndex
        {
            get { return selectedResourceIndex; }
            set
            {
                if (selectedResourceIndex != value)
                {
                    selectedResourceIndex = value;
                    OnPropertyChanged("SelectedResourceIndex");
                    OnPropertyChanged("SelectedResource");
                }
            }
        }
        #endregion

        #region Extenal Call
        public void Refresh()
        { 
        }
         
        #endregion

        #region Helper Methods

        // ToDo:
        protected void LoadLastDownloadInfo()
        {
            if(Directory.Exists(Util.DownloadFileInfoPath))
            {
                var allDownloadInfoFileNames = Directory.GetFiles(Util.DownloadFileInfoPath, "*" + Util.DownloadFileInfoExtension, SearchOption.TopDirectoryOnly);
                foreach (var downloadInfoFileName in allDownloadInfoFileNames)
                {
                    var infoFileName = Path.GetFileNameWithoutExtension(downloadInfoFileName);
                    var downloadDTO = DownloadDTO.LoadFromInfoFile(infoFileName);
                    AddDownload(downloadDTO,false);
                }
            }
        }

        public void DownloadAll()
        {
            foreach (var downloadResource in DownloadResources)
            {
                Action action = downloadResource.DownloadObject.Download;
                var threadStart = new ThreadStart(() => { Dispatcher.CurrentDispatcher.Invoke(action, DispatcherPriority.Render); });
                var newThread = new Thread(threadStart);
                newThread.Start();
            }
        }

        #endregion

        #region External Methods
        public void DeleteDownload(DownloadDTO downloadDTO,bool removeFile=true)
        {
            try
            {
                if (downloadDTO == null || downloadDTO.DownloadObject == null)
                    return;

                DownloadResources.Remove(downloadDTO);
                downloadDTO.Remove(removeFile);
                downloadDTO = null;
            }
            catch { }                        
        }

        public void PauseDownload(DownloadDTO downloadDTO)
        {
            try
            {
                if (downloadDTO == null || downloadDTO.DownloadObject == null)
                    return;

                if (DownloadResources.Any(r => r.ID == downloadDTO.ID))
                {
                    downloadDTO.Pause();
                    OnPropertyChanged("SelectedStatusDownloads");
                }
            }
            catch { }  
        }

        public void AddDownload(AudioResource resource,bool enforceDownload=true)
        {
            var downloadDTO = DownloadResources.SingleOrDefault(r => r.ID == resource.ID);
            if (downloadDTO == null)
            {
                downloadDTO = new DownloadDTO(resource); 
            }
            AddDownload(downloadDTO, enforceDownload);
        }

        public void AddDownload(DownloadDTO downloadDTO, bool enforceDownload = true)
        {
            if (downloadDTO == null || downloadDTO.DownloadObject==null)
                return;

            downloadDTO.DownloadObject.DownloadCompleted += new EventHandler<EventArgs>(DownloadObject_DownloadCompleted);
            if (!DownloadResources.Any(r => r.ID == downloadDTO.ID))
            {
                var completedResource = DownloadResources.FirstOrDefault(r => r.Status == DownloadStatus.Completed);
                if (completedResource != null)
                {
                    var index = DownloadResources.IndexOf(completedResource);
                    DownloadResources.Insert(index, downloadDTO);
                }
                else
                {
                    DownloadResources.Add(downloadDTO);
                }

                if (DownloadResources.Where(d => d.Status == DownloadStatus.Downloading).Count() < Util.MaxDownloadNum && enforceDownload)
                {
                    BackgroundWorker worker = new BackgroundWorker();
                    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
                    worker.RunWorkerAsync(downloadDTO.DownloadObject);                   
                }
                else
                { 
                }
            }
            else
            {
                if (enforceDownload && (downloadDTO.Status == DownloadStatus.Waiting || downloadDTO.Status== DownloadStatus.Paused))
                {
                    BackgroundWorker worker = new BackgroundWorker();
                    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
                    worker.RunWorkerAsync(downloadDTO.DownloadObject); 
                }
            } 
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            //Thread.Sleep(1000); // This is very important, the reason I haven't found
            var download = e.Argument as IDownload;
            Action action = download.Download;
            action.BeginInvoke((IAsyncResult result) => { },null);
        } 

        void DownloadObject_DownloadCompleted(object sender, EventArgs e)
        {
            Thread.Sleep(100);
            OnPropertyChanged("SelectedStatusDownloads");
            while (DownloadResources.Count(r => r.Status == DownloadStatus.Downloading) < Util.MaxDownloadNum)
            {
                var nextDownloadResource = DownloadResources.FirstOrDefault(r => r.Status == DownloadStatus.Waiting);// (r => r.Status != DownloadStatus.Completed && r.Status != DownloadStatus.Downloading);
                if (nextDownloadResource != null)
                {
                    if (nextDownloadResource.DownloadObject != null)
                    {
                        BackgroundWorker worker = new BackgroundWorker();
                        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
                        worker.RunWorkerAsync(nextDownloadResource.DownloadObject);
                        Thread.Sleep(100);
                    }
                }
                else 
                {
                    break;
                }
            }
        }
         
        #endregion

        #region Commands
        public ICommand ContinueDownloadCommand { get; set; }
        protected virtual void OnContinueDownload(object args)
        {
            try
            {
                var id = (int)args;
                var downloadDTO = DownloadResources.SingleOrDefault(r => r.ID == id);
                AddDownload(downloadDTO);
            }
            catch { }
        }

        public ICommand SelectAllResourceCommand { get; set; }
        protected virtual void OnSelectAllResource(object args)
        {
            var isChecked = (bool)args;
            foreach (var download in DownloadResources)
            {
                download.Resource.Selected = isChecked;
            }
            OnPropertyChanged("DownloadResources");
        }

        public ICommand SelectOthersResourceCommand { get; set; }
        protected virtual void OnSelectOthersResource(object args)
        {
            foreach (var download in DownloadResources)
            {
                download.Resource.Selected = !download.Resource.Selected;
            }
            OnPropertyChanged("DownloadResources");
        }


        public ICommand DownloadSelectedCommand { get; set; }
        protected virtual void OnDownloadSelected(object args)
        {
            foreach (var download in DownloadResources)
            {
                if (download.Resource.Selected)
                {
                    AddDownload(download.Resource, true);
                }
            }
        }

        public ICommand DeleteSelectedCommand { get; set; }
        protected virtual void OnDeleteSelected(object args)
        {
            var allSelectedDownload = DownloadResources.Where(d => d.Resource.Selected).ToArray();
            foreach (var download in allSelectedDownload)
            {
                DeleteDownload(download);
            } 
        }

        public ICommand PauseSelectedCommand { get; set; }
        protected virtual void OnPauseSelected(object args)
        {
            foreach (var download in DownloadResources)
            {
                if (download.Resource.Selected)
                {
                    PauseDownload(download);
                }
            }
        }

        public ICommand OpenDownloadLocationCommand { get; set; }
        protected virtual void OnOpenDownloadLocation(object args) 
        { 
            string path = XbdConfiguration.Load().SaveFilePath; 
            System.Diagnostics.Process.Start("explorer.exe", path);

        }
        #endregion

    }

}
